package com.wzdigit.wms.wms.service.transfer;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.wzdigit.framework.Shift;
import com.wzdigit.wms.basic.client.request.transfer.SplitAndMergerCommitReq;
import com.wzdigit.wms.basic.client.request.transfer.SplitAndMergerScanReq;
import com.wzdigit.wms.basic.client.response.Result;
import com.wzdigit.wms.basic.client.response.transfer.SnLocationResponse;
import com.wzdigit.wms.basic.domain.basic.Location;
import com.wzdigit.wms.basic.domain.basic.Sku;
import com.wzdigit.wms.basic.domain.dtos.inventory.SnListInfoDto;
import com.wzdigit.wms.basic.domain.dtos.transfer.TransferLocationSnDto;
import com.wzdigit.wms.basic.domain.inventory.Goods;
import com.wzdigit.wms.basic.domain.inventory.GoodsPkg;
import com.wzdigit.wms.basic.enums.GoodsStatusEnum;
import com.wzdigit.wms.basic.mapper.core.basic.LocationMapper;
import com.wzdigit.wms.basic.mapper.core.basic.SkuMapper;
import com.wzdigit.wms.basic.mapper.core.basic.WarehouseMapper;
import com.wzdigit.wms.basic.mapper.core.inventory.GoodsMapper;
import com.wzdigit.wms.basic.mapper.core.inventory.GoodsPkgMapper;
import com.wzdigit.wms.common.entity.StatusCode;
import lombok.extern.slf4j.Slf4j;
import net.dreamlu.mica.core.utils.CollectionUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @ClassName SplitAndMergerService
 * @Description 在库模块-拆并板
 * @Author LHR-PC
 * @Date 2021/4/1 9:25
 * @Version
 **/
@Service
@Slf4j
public class SplitAndMergerService {

    @Autowired
    private GoodsMapper goodsMapper;

    @Autowired
    private GoodsPkgMapper goodsPkgMapper;

    @Autowired
    private SkuMapper skuMapper;

    @Autowired
    private WarehouseMapper warehouseMapper;

    @Autowired
    private LocationMapper locationMapper;

    @Lazy
    @Autowired
    private TransferLocationService transferLocationService;


    /**
     * 扫描条码
     *
     * @param splitAndMergerScanReq
     * @return
     */
    public SnLocationResponse scanSn(SplitAndMergerScanReq splitAndMergerScanReq) {
        String sn = splitAndMergerScanReq.getSn();
        Integer scanSnType = splitAndMergerScanReq.getScanSnType(); //扫描条码类型：源条码传0，目标条码传1
        Integer scanType = splitAndMergerScanReq.getScanType(); //扫描类型：扫描源条码时 单台传0/整版传1 ,扫描目标条码时 同级传0/上级传1


        //根据条码判断条码类型并返回对应货品和包装信息
        TransferLocationSnDto snTypeAndInfo = this.getSnTypeAndInfoWithSplit(sn);

        SnLocationResponse snLocationResponse = new SnLocationResponse(); //返回实体

        Goods goods = snTypeAndInfo.getGoods();

        int waitUpCode = GoodsStatusEnum.WAIT_UP.getCode();//待上架--2
        int hasUpCode = GoodsStatusEnum.HAS_UP.getCode();//已上架--3
        int waitDowmCode = GoodsStatusEnum.WAIT_DOWM.getCode();//已下架--4
        List<Integer> allowStatusList = Arrays.asList(waitUpCode, hasUpCode, waitDowmCode);

        //添加校验
        if (goods != null) {
            Integer status = goods.getStatus();
            if (!allowStatusList.contains(status)) {

                GoodsStatusEnum goodsStatusEnum = GoodsStatusEnum.getEnumByCode(status);

                String msg = String.format("拆并包要求SN为[待上架]/[已上架]/[已下架]状态，当前为【%s】状态", goodsStatusEnum.getEnumName());
                Shift.fatal(StatusCode.BUSINESS_ERR, msg);
            }
        }

        GoodsPkg goodsPkg = snTypeAndInfo.getGoodsPkg();
        Boolean isLeaf = snTypeAndInfo.getIsLeaf(); //是否叶子节点

        GoodsPkg fatherGoods = null;
        ArrayList<SnListInfoDto> snListInfoDtos = new ArrayList<>();
        SnListInfoDto snListInfoDto = new SnListInfoDto(); //明细列表里的自己

        String goodSn = snTypeAndInfo.getGoodsSn();
        String pkgSn = snTypeAndInfo.getPkgSn();
        int snType = snTypeAndInfo.getSnType(); //条码类型，0为goodsSn , 1为pkgSn
        Integer pkgId = snTypeAndInfo.getPkgId();
        Integer pkgLevel = snTypeAndInfo.getPkgLevel(); //包装等级，默认 0 货品条码
        Sku sku = snTypeAndInfo.getSku();
        Location location = snTypeAndInfo.getLocation();
        Integer leafPkgId = snTypeAndInfo.getLeafPkgId(); //叶子节点
        String pkgUrl = snTypeAndInfo.getPkgUrl(); //路径
        List<Goods> goodsList = snTypeAndInfo.getGoodsList(); //条码列表，snType == 1 才有

        if (goodsList != null) {
            //添加校验
            for (Goods goods1 : goodsList) {
                if (goods1 != null) {
                    Integer status = goods1.getStatus();
                    if (!allowStatusList.contains(status)) {
                        GoodsStatusEnum goodsStatusEnum = GoodsStatusEnum.getEnumByCode(status);
                        String msg = String.format("拆并包要求SN为[待上架]/[已上架]/[已下架]状态，条码【%s】当前为【%s】状态", goods1.getGoodsSn(), goodsStatusEnum.getEnumName());
                        Shift.fatal(StatusCode.BUSINESS_ERR, msg);
                    }
                }
            }
        }


        Integer whsId = snTypeAndInfo.getWhsId();

        snLocationResponse.setWhsId(whsId);
        if (pkgId != null) {
            if (snType == 0) {
                //goods条码，上级条码是goods里的pkgId
                fatherGoods = goodsPkgMapper.selectOne(new QueryWrapper<>(new GoodsPkg().setGoodsPkgId(goods.getPkgId())));
            } else {
                //pkg条码，上级条码是goodsPkg里的fatherPkgId
                fatherGoods = goodsPkgMapper.selectOne(new QueryWrapper<>(new GoodsPkg().setGoodsPkgId(goodsPkg.getFatherPkgId())));
            }
            if (fatherGoods != null) {
                if (scanType == 0) {
                    snLocationResponse.setSourceFatherPkgSn(fatherGoods.getPkgSn());
                } else {
                    snLocationResponse.setTargetFatherPkgSn(fatherGoods.getPkgSn());
                }
                snLocationResponse.setFatherPkgId(fatherGoods.getGoodsPkgId());
            }
        }
        snLocationResponse.setSnLevel(String.valueOf(pkgLevel));


        //扫描源条码
        if (scanSnType == 0) {
            if (pkgId == null) {
                if (scanType == 1) {
                    Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "条码没有包装信息，请切换至单台扫描");
                }
                //条码没有pkgID,直接返回条码信息
                snLocationResponse.setQty(goods.getQty());
                snLocationResponse.setWhsId(goods.getWhsId());
                snLocationResponse.setSkuCode(sku.getSkuCode());
                snLocationResponse.setSourceLocId(goods.getLocId());
                snLocationResponse.setSourceLocCode(location == null ? null : location.getLocCode());
                snLocationResponse.setSnLevel(String.valueOf(pkgLevel));
                this.scanGoodsSnWithSingle(goods, goodsPkg, snListInfoDtos, snListInfoDto, sku, location);
                snLocationResponse.setSnList(snListInfoDtos);
                return snLocationResponse;
            }
            //source 单台整版通用
            snLocationResponse.setWhsId(goods.getWhsId());
            snLocationResponse.setSkuCode(sku.getSkuCode());
            snLocationResponse.setSourceLocId(goods.getLocId());
            snLocationResponse.setSourceLocCode(location == null ? null : location.getLocCode());
            snLocationResponse.setFatherPkgSn(fatherGoods == null ? "" : fatherGoods.getPkgSn());
            snLocationResponse.setSourceFatherPkgSn(fatherGoods == null ? "" : fatherGoods.getPkgSn());
            //条码信息
            //单台
            if (scanType == 0) {
                if (snType == 0) {
                    //扫的goodSn
                    this.scanGoodsSnWithSingle(goods, goodsPkg, snListInfoDtos, snListInfoDto, sku, location);
                    snLocationResponse.setSnList(snListInfoDtos);
                    return snLocationResponse;
                } else if (snType == 1) {
                    //扫的pkgSn
                    this.scanPkgSnWithSingle(goodsPkg, fatherGoods, snListInfoDtos, snListInfoDto, goodsList);
                    snLocationResponse.setSnList(snListInfoDtos);
                    return snLocationResponse;
                }
            }
            //整版
            else if (scanType == 1) {
                if (snType == 0) {
                    //扫的goodsSn
                    //通过goods里的包装ID找到同级条码
                    List<Goods> goodsListSameLevel = goodsMapper.selectList(new QueryWrapper<>(new Goods().setPkgId(pkgId)));
                    for (Goods singleGooodsInList : goodsListSameLevel) {
//                        Location location1 = locationMapper.selectOne(new QueryWrapper<>(new Location().setLocId(singleGooodsInList.getLocId())));
//                        if (location1 == null) {
//                            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "条码" + singleGooodsInList.getGoodsSn() + "库位不存在，请先维护");
//                        }
                        Sku sku1 = skuMapper.selectById(singleGooodsInList.getSkuId());
                        if (sku1 == null) {
                            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "条码" + singleGooodsInList.getGoodsSn() + "货品不存在");
                        }
                        SnListInfoDto snListInfoDto1 = new SnListInfoDto();
                        this.scanGoodsSnWithSingle(singleGooodsInList, goodsPkg, snListInfoDtos, snListInfoDto1, sku1, location);
                        snLocationResponse.setSnList(snListInfoDtos);
                    }
                } else {
                    //扫的pkgSn
                    if (fatherGoods == null) {
                        Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "该条码已是最顶级包装，查无同层级包装条码，请切换至单台扫描");
                    }
                    List<GoodsPkg> goodsPkgs = goodsPkgMapper.selectList(new QueryWrapper<>(new GoodsPkg().setFatherPkgId(goodsPkg.getFatherPkgId())));
                    for (GoodsPkg pkg : goodsPkgs) {
                        List<Goods> goodsList1 = goodsMapper.selectList(new QueryWrapper<>(new Goods().setPkgId(pkg.getGoodsPkgId())));
                        SnListInfoDto snListInfoDto1 = new SnListInfoDto();
                        this.scanPkgSnWithSingle(pkg, fatherGoods, snListInfoDtos, snListInfoDto1, goodsList1);
                    }
                    snLocationResponse.setSnList(snListInfoDtos);
                    return snLocationResponse;
                }
            }
        } else { //扫描目标条码
            if (pkgId == null || pkgSn == null) {
                Shift.fatal(StatusCode.INVALID_MODEL_FIELDS, "目标条码包装不能为空");
            }
            if (scanType == 0) {
                //同级
                if (snType == 0) {
                    //扫的goodsSn
                    log.info("扫描目标条码{}，条码类型goods条码，扫描方式同级", sn);
                    Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "条码不是包装条码，请切换至上级扫描方式");
//                    snLocationResponse.setTargetPkgQty(goods.getQty());
//                    snLocationResponse.setTargetLocId(goods.getLocId());
//                    snLocationResponse.setTargetLocCode(location.getLocCode());
//                    snLocationResponse.setTargetFatherPkgSn(fatherGoods == null ? "" : fatherGoods.getPkgSn());
//                    return snLocationResponse;
                } else {
                    //扫的PkgSn
                    log.info("扫描目标条码{}，条码类型pkg条码，扫描方式同级", sn);
                    goodsList = goodsMapper.selectList(new QueryWrapper<>(new Goods().setPkgId(pkgId)));
                    BigDecimal pkgQty = goodsList.stream().filter(g -> g.getQty() != null).map(Goods::getQty).reduce((x, y) -> x.add(y)).get();
                    snLocationResponse.setTargetPkgQty(pkgQty);
                    snLocationResponse.setTargetLocId(goods.getLocId());
                    snLocationResponse.setTargetLocCode(location.getLocCode());
                    snLocationResponse.setTargetFatherPkgSn(pkgSn); //同级父亲就是自己
                }
            } else {
                //上级
                if (snType == 0) {
                    //扫的goodsSn
                    log.info("扫描目标条码{}，条码类型goods条码，扫描方式上级", sn);
                    goodsList = goodsMapper.selectList(new QueryWrapper<>(new Goods().setPkgId(pkgId)));
                    BigDecimal pkgQty = goodsList.stream().filter(g -> g.getQty() != null).map(Goods::getQty).reduce((x, y) -> x.add(y)).get();
                    snLocationResponse.setTargetPkgQty(pkgQty);
                    snLocationResponse.setTargetLocId(goods.getLocId());
                    snLocationResponse.setTargetLocCode(location == null ? null : location.getLocCode());
                    snLocationResponse.setTargetFatherPkgSn(fatherGoods == null ? "" : fatherGoods.getPkgSn());
                } else {
                    //扫的pkgSn
                    log.info("扫描目标条码{}，条码类型pkg条码，扫描方式上级", sn);
                    if (fatherGoods == null) {
                        Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "该条码已是最上级包装，请切换至同级扫描");
                    }
                    BigDecimal pkgQty = goodsList.stream().filter(g -> g.getQty() != null).map(Goods::getQty).reduce((x, y) -> x.add(y)).get();
                    snLocationResponse.setTargetPkgQty(pkgQty);
                    snLocationResponse.setTargetLocId(goods.getLocId());
                    snLocationResponse.setTargetLocCode(location.getLocCode());
                    snLocationResponse.setTargetFatherPkgSn(fatherGoods == null ? "" : fatherGoods.getPkgSn());
                }
            }
        }
        return snLocationResponse;
    }

    /**
     * 扫描条码类型为GoodsSn,并且单台
     *
     * @param goods
     * @param goodsPkg
     * @param snListInfoDtos
     * @param snListInfoDto
     * @param sku
     * @param location
     */
    private void scanGoodsSnWithSingle(Goods goods, GoodsPkg goodsPkg, ArrayList<SnListInfoDto> snListInfoDtos, SnListInfoDto snListInfoDto, Sku sku, Location location) {
        snListInfoDto.setPkgNo(goods.getGoodsSn());
        snListInfoDto.setSkuCode(sku.getSkuCode());
        snListInfoDto.setPoNo(goods.getPoNo());
        snListInfoDto.setLocCode(location == null ? null : location.getLocCode());
        snListInfoDto.setLocId(location == null ? null : location.getLocId());
        snListInfoDto.setLocDesc(location == null ? null : location.getLocDesc());
        snListInfoDto.setSkuDesc(sku.getDescCn());
        snListInfoDto.setQty(goods.getQty());
        if (goodsPkg != null) {
            snListInfoDto.setFatherPkgNo(goodsPkg.getPkgSn());
            snListInfoDto.setPkgCategory(goodsPkg.getPkgCategory());
        }
        snListInfoDtos.add(snListInfoDto);
    }

    /**
     * 扫描条码类型为pkgSn,并且单台
     *
     * @param goodsPkg
     * @param fatherGoods
     * @param snListInfoDtos
     * @param snListInfoDto
     * @param goodsList
     */
    private void scanPkgSnWithSingle(GoodsPkg goodsPkg, GoodsPkg fatherGoods, ArrayList<SnListInfoDto> snListInfoDtos, SnListInfoDto snListInfoDto, List<Goods> goodsList) {
        snListInfoDto.setPkgNo(goodsPkg.getPkgSn());
        List<Integer> skuIds = goodsList.stream().map(Goods::getSkuId).collect(Collectors.toList());
        List<String> poNos = goodsList.stream().map(Goods::getPoNo).collect(Collectors.toList());
        List<Integer> locIds = goodsList.stream().map(Goods::getLocId).collect(Collectors.toList());
        List<String> locNos = new ArrayList<>();
        try {
            locIds.forEach(l -> locNos.add(locationMapper.selectOne(new QueryWrapper<>(new Location().setLocId(l))).getLocCode()));
        } catch (Exception e) {
            e.printStackTrace();
            if (NullPointerException.class.isInstance(e)) {
                Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "有库位不存在");
            }
        }
        List<String> skuCodes = new ArrayList<>();
        List<String> skuDescs = new ArrayList<>();
        skuIds.forEach(i -> {
            Sku skuRecord = skuMapper.selectOne(new QueryWrapper<>(new Sku().setSkuId(i)));
            if (skuRecord != null) {
                skuCodes.add(skuRecord.getSkuCode());
                skuDescs.add(skuRecord.getDescCn());
            }
        });
        List<String> pos = poNos.stream().distinct().collect(Collectors.toList());
        List<String> locs = locNos.stream().distinct().collect(Collectors.toList());
        List<String> sks = skuCodes.stream().distinct().collect(Collectors.toList());
        List<String> sds = skuDescs.stream().distinct().collect(Collectors.toList());
        snListInfoDto.setSkuCode(String.join("、", sks));
        snListInfoDto.setPoNo(String.join("、", pos));
        snListInfoDto.setLocCode(String.join("、", locs));
        snListInfoDto.setSkuDesc(String.join("、", sds));
        snListInfoDto.setFatherPkgNo(fatherGoods == null ? "" : fatherGoods.getPkgSn());
        snListInfoDto.setPkgCategory(goodsPkg.getPkgCategory());
        snListInfoDtos.add(snListInfoDto);
    }


    /**
     * 根据条码判断条码类型并返回对应货品和包装信息(移库)
     *
     * @param sn
     * @return
     */
    public TransferLocationSnDto getSnTypeAndInfo(String sn) {
        TransferLocationSnDto transferLocationSnDto = new TransferLocationSnDto();

        Goods goods = null;
        GoodsPkg goodsPkg = null;
        Boolean isLeaf = null; //是否叶子节点（针对扫描条码而言，如果是Null说明是条码）
        List<Goods> goodsList = new ArrayList<>(); //条码列表
        ArrayList<SnListInfoDto> snListInfoDtos = new ArrayList<>();

        String goodSn = "";
        String pkgSn = "";
        int snType = 0; //条码类型，0为goodsSn , 1为pkgSn
        Integer pkgId = null;
        Integer pkgLevel = 0; //包装等级，默认 0 货品条码
        String pkgUrl = "";
        Integer leafPkgId = null; //叶子节点ID

        //查goods表
        goods = goodsMapper.selectOne(new QueryWrapper<>(new Goods().setGoodsSn(sn)));
        if (goods != null) {
            //SN是GoodsSn
            goodSn = goods.getGoodsSn();
            pkgId = goods.getPkgId();
            if (pkgId == null) {
                //条码没有包装信息，直接返回条码信息
                if (goods.getLocId() == null) {
                    Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "条码查无库位信息");
                }
                //如果是移库操作，则必须要有库位，如果是拆并包，则可以没有
                Location location = locationMapper.selectOne(new QueryWrapper<>(new Location().setLocId(goods.getLocId())));
                if (location == null) {
                    Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "库位不存在");
                }
                Sku sku = skuMapper.selectOne(new QueryWrapper<>(new Sku().setSkuId(goods.getSkuId())));
                if (sku == null) {
                    Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "物料编码不存在");
                }

                transferLocationSnDto.setGoods(goods)
                        .setPkgLevel(pkgLevel)
                        .setSnType(snType)
                        .setGoodsSn(goodSn)
                        .setLocation(location)
                        .setSku(sku)
                        .setWhsId(goods.getWhsId());
                return transferLocationSnDto;
//                Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "该SN查无对应包装信息");
            }
            //用Goods里的PKGID查对应包装信息
            goodsPkg = goodsPkgMapper.selectById(goods.getPkgId());
            if (goodsPkg == null) {
                Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "该SN查无对应包装信息");
            }
            pkgUrl = goodsPkg.getUrl();
            isLeaf = null;
        }
        //查PKG表
        if (goodsPkg == null) {
            goodsPkg = goodsPkgMapper.selectOne(new QueryWrapper<>(new GoodsPkg().setPkgSn(sn)));
            if (goodsPkg == null) {
                Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "查无条码信息");
            }
            if (goodsPkg.getPkgSn() != null) {
                //SN是PKG_SN
                pkgId = goodsPkg.getGoodsPkgId();
                pkgLevel = goodsPkg.getPkgLevel();
                Integer leaf = goodsPkg.getIsLeaf();
                pkgSn = goodsPkg.getPkgSn();
                snType = 1;
                pkgUrl = goodsPkg.getUrl();
                if (leaf == 0) {
                    isLeaf = false;
                    //如果不是叶子节点，直接通过路径拿到叶子节点
                    String[] split = pkgUrl.split("-");
                    leafPkgId = Integer.parseInt(split[split.length]);
                    //通过叶子节点查货品信息
                    goodsList = goodsMapper.selectList(new QueryWrapper<>(new Goods().setPkgId(pkgId)));
                    if (CollectionUtil.isEmpty(goodsList)) {
                        //
                        Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "该包装条码查不到对应货品信息");
                        goods = goodsList.get(0);
                    }
                } else {
                    isLeaf = true;
                    goodsList = goodsMapper.selectList(new QueryWrapper<>(new Goods().setPkgId(pkgId)));
                    if (CollectionUtil.isEmpty(goodsList)) {
                        //
                        Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "该包装条码查不到对应货品信息");
                    }
                    goods = goodsList.get(0);
                }
            }
        }


        if (goodSn.equals("") && pkgSn.equals("")) {
            Shift.fatal("SN[" + sn + "]信息不存在!");
        }
        if (pkgId == null) {
            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "没有找到该条码的包装信息");
        }
        //到这一步不管是扫的SN还是扫的PKG_SN，goodsPkg和goods都是必有的,但goodList不一定有,snType = 1时，才有GoodsList
        if (goodsPkg.getIsLeaf() == 1) {
            isLeaf = true;
        } else if (goodsPkg.getIsLeaf() == 0) {
            //nothing to do
        } else {
            Shift.fatal(StatusCode.BUSINESS_ERR, "是否叶子节点不能为空");
        }
        Sku sku = skuMapper.selectOne(new QueryWrapper<>(new Sku().setSkuId(goods.getSkuId())));
        if (sku == null) {
            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "物料编码不存在");
        }
        if (goods.getLocId() == null) {
            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "条码查无库位信息");
        }
        Location location = locationMapper.selectOne(new QueryWrapper<>(new Location().setLocId(goods.getLocId())));
        if (location == null) {
            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "库位不存在");
        }
        Integer whsId = locationMapper.selectWhsIdBylocId(location.getLocId());
        if (whsId == null) {
            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "仓库不存在");
        }
        if (snType == 1) {
            pkgLevel = goodsPkg.getPkgLevel();
        }
        transferLocationSnDto.setGoods(goods)
                .setGoodsPkg(goodsPkg)
                .setIsLeaf(isLeaf)
                .setPkgLevel(pkgLevel)
                .setSnType(snType)
                .setGoodsSn(goodSn)
                .setPkgSn(pkgSn)
                .setLocation(location)
                .setGoodsList(goodsList)
                .setLeafPkgId(leafPkgId)
                .setPkgUrl(pkgUrl)
                .setSku(sku)
                .setPkgId(pkgId)
                .setWhsId(whsId);
        return transferLocationSnDto;
    }

    /**
     * 根据条码判断条码类型并返回对应货品和包装信息(拆并包)
     *
     * @param sn
     * @return
     */
    public TransferLocationSnDto getSnTypeAndInfoWithSplit(String sn) {
        TransferLocationSnDto transferLocationSnDto = new TransferLocationSnDto();

        Goods goods = null;
        GoodsPkg goodsPkg = null;
        Boolean isLeaf = null; //是否叶子节点（针对扫描条码而言，如果是Null说明是条码）
        List<Goods> goodsList = new ArrayList<>(); //条码列表
        ArrayList<SnListInfoDto> snListInfoDtos = new ArrayList<>();

        String goodSn = "";
        String pkgSn = "";
        int snType = 0; //条码类型，0为goodsSn , 1为pkgSn
        Integer pkgId = null;
        Integer pkgLevel = 0; //包装等级，默认 0 货品条码
        String pkgUrl = "";
        Integer leafPkgId = null; //叶子节点ID
        Location location = null;

        //查goods表
        goods = goodsMapper.selectOne(new QueryWrapper<>(new Goods().setGoodsSn(sn)));


        if (goods != null) {
            //SN是GoodsSn
            goodSn = goods.getGoodsSn();
            pkgId = goods.getPkgId();
            if (pkgId == null) {
//                Shift.fatal(StatusCode.BUSINESS_ERR,"该条码没有上级包装");

                //条码没有包装信息，直接返回条码信息
//                if (goods.getLocId() == null) {
//                    Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "条码查无库位信息");
//                }
                //如果是移库操作，则必须要有库位，如果是拆并包，则可以没有
                if (goods.getLocId() != null) {
                    location = locationMapper.selectOne(new QueryWrapper<>(new Location().setLocId(goods.getLocId())));
                }
                Sku sku = skuMapper.selectOne(new QueryWrapper<>(new Sku().setSkuId(goods.getSkuId())));
                if (sku == null) {
                    Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "物料编码不存在");
                }

                transferLocationSnDto.setGoods(goods)
                        .setPkgLevel(pkgLevel)
                        .setSnType(snType)
                        .setGoodsSn(goodSn)
                        .setLocation(location)
                        .setSku(sku)
                        .setWhsId(goods.getWhsId())
                        .setGoods(goods);
                return transferLocationSnDto;
//                Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "该SN查无对应包装信息");
            }
            //用Goods里的PKGID查对应包装信息
            goodsPkg = goodsPkgMapper.selectById(goods.getPkgId());
            if (goodsPkg == null) {
                Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "该SN查无对应包装信息");
            }
            pkgUrl = goodsPkg.getUrl();
            isLeaf = null;
        }
        //查PKG表
        if (goodsPkg == null) {
            goodsPkg = goodsPkgMapper.selectOne(new QueryWrapper<>(new GoodsPkg().setPkgSn(sn)));
            if (goodsPkg == null) {
                Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "查无条码信息");
            }
            if (goodsPkg.getPkgSn() != null) {
                //SN是PKG_SN
                pkgId = goodsPkg.getGoodsPkgId();
                pkgLevel = goodsPkg.getPkgLevel();
                Integer leaf = goodsPkg.getIsLeaf();
                pkgSn = goodsPkg.getPkgSn();
                snType = 1;
                pkgUrl = goodsPkg.getUrl();
                if (leaf == 0) {
                    isLeaf = false;
                    //如果不是叶子节点，直接通过路径拿到叶子节点
                    String[] split = pkgUrl.split("-");
                    leafPkgId = Integer.parseInt(split[split.length]);
                    //通过叶子节点查货品信息
                    goodsList = goodsMapper.selectList(new QueryWrapper<>(new Goods().setPkgId(pkgId)));
                    if (CollectionUtil.isEmpty(goodsList)) {
                        //
                        Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "该包装条码查不到对应货品信息");
                        goods = goodsList.get(0);
                    }
                } else {
                    isLeaf = true;
                    goodsList = goodsMapper.selectList(new QueryWrapper<>(new Goods().setPkgId(pkgId)));
                    if (CollectionUtil.isEmpty(goodsList)) {
                        //
                        Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "该包装条码查不到对应货品信息");
                    }
                    goods = goodsList.get(0);
                }
            }
        }


        if (goodSn.equals("") && pkgSn.equals("")) {
            Shift.fatal("SN[" + sn + "]信息不存在!");
        }
        if (pkgId == null) {
            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "没有找到该条码的包装信息");
        }
        //到这一步不管是扫的SN还是扫的PKG_SN，goodsPkg和goods都是必有的,但goodList不一定有,snType = 1时，才有GoodsList
        Integer whsId = goods.getWhsId();
        if (whsId == null) {
            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "仓库不能为空");
        }

        if (goodsPkg.getIsLeaf() == 1) {
            isLeaf = true;
        } else if (goodsPkg.getIsLeaf() == 0) {
            //nothing to do
        } else {
            Shift.fatal(StatusCode.BUSINESS_ERR, "是否叶子节点不能为空");
        }
        Sku sku = skuMapper.selectOne(new QueryWrapper<>(new Sku().setSkuId(goods.getSkuId())));
        if (sku == null) {
            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "物料编码不存在");
        }
//        if (goods.getLocId() == null) {
//            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "条码查无库位信息");
//        }
        if (goods.getLocId() != null) {
            location = locationMapper.selectOne(new QueryWrapper<>(new Location().setLocId(goods.getLocId())));
//            Integer whsId = locationMapper.selectWhsIdBylocId(location.getLocId());
        }
//        if (location == null) {
//            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "库位不存在");
//        }
        if (snType == 1) {
            pkgLevel = goodsPkg.getPkgLevel();
        }
        transferLocationSnDto.setGoods(goods)
                .setGoodsPkg(goodsPkg)
                .setIsLeaf(isLeaf)
                .setPkgLevel(pkgLevel)
                .setSnType(snType)
                .setGoodsSn(goodSn)
                .setPkgSn(pkgSn)
                .setLocation(location)
                .setGoodsList(goodsList)
                .setLeafPkgId(leafPkgId)
                .setPkgUrl(pkgUrl)
                .setSku(sku)
                .setPkgId(pkgId)
                .setWhsId(whsId);
        return transferLocationSnDto;
    }

    /**
     * 1、拆并包服务
     * List<源条码>、目标条码
     * 	控制点
     * 1)	List<源条码> 与 目标条码必须在同一个仓库内，并且物料是一样的。
     * 2)	拆并板动作，源条码必须是同等级条码，并且属于目标条码的下级条码，否则无法拆并板。
     * 3)	List<源条码>的条码允许从不同上级包装、不同库位上，但 源条码上级包装不允许等于“目标条码”
     * 	逻辑
     * 把源条码从上级包装条码拆分下来，挂靠在 目标条码的 下面，原条码还有下面条码，也一并挂靠。调 更新包装层级服务 (这个服务未实现，要写好，补充到腾讯文档)
     * 如果涉及到“源条码”与“目标条码”在不同库位的，要执行2、移库服务。移库的数量要统计“源条码”代表的全部数量。
     *
     * 2、移库服务
     * 	入参
     * List<源条码>、目标库位
     * 	控制点
     * 1)	List<源条码> 与 目标条码必须在同一个仓库内。
     * 2)	List<源条码>的库位不能等于目标库位
     * 3)	允许不同物料、不同库位信息同时移库
     *
     * 	逻辑
     * 调更新货品服务更新货品清单表(WMS_GOODS) 的List<源条码>的库位为“目标库位”
     * 调库存更新服务更新库位库存、库区库存表。源库位减少、目标库位增加。
     *
     * 3、	更新包装层级服务
     * 货品包装信息(WMS_GOODS_PKG)、货品清单表(WMS_GOODS)的包装层级关系变更。包装层级关系：货品清单表PKG_ID、货品包装信息的GOODS_PKG_ID。
     */


    /**
     * 提交
     *
     * @param splitAndMergerCommitReq
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public Result<?> commit(SplitAndMergerCommitReq splitAndMergerCommitReq, String loginName) throws Exception {
        //TODO 测试代码
//        Shift.fatal("业务测试，待更新下一个版本开放使用权限");

        String sourceFatherPkgSn = splitAndMergerCommitReq.getSourceFatherPkgSn(); //源上级包装
        Integer targetLocId = splitAndMergerCommitReq.getTargetLocId(); //目标库位ID
        String targetLocCode = splitAndMergerCommitReq.getTargetLocCode(); //目标库位
        List<SnListInfoDto> snList = splitAndMergerCommitReq.getSnList(); //待拆包条码
        Integer snLevel = splitAndMergerCommitReq.getSnLevel();
        String targetSn = splitAndMergerCommitReq.getTargetFatherPkgSn();
        Integer targetWhsId = splitAndMergerCommitReq.getWhsId();


        //根据目标条码查条码
        Goods targetGoods = goodsMapper.selectBySn(splitAndMergerCommitReq.getTargetSn());

        if (targetWhsId == null) {
            Shift.fatal(StatusCode.SERVER_NOT_FOUND_ERROR, "目标仓库不能为空");
        }


        if (CollectionUtil.isEmpty(snList)) {
            Shift.fatal(StatusCode.INVALID_MODEL_FIELDS, "条码列表为空");
        }


        if (targetSn == null || targetSn.equals("")) {
            for (SnListInfoDto snListInfoDto : snList) {
                if (snListInfoDto.getFatherPkgNo() == null || snListInfoDto.getFatherPkgNo().equals("")) {
                    //拆包不是拆自己，是将自己从上级包装拆出来
                    Shift.fatal(StatusCode.SERVER_MODIFY_ERROR, "该条码已是最上级包装，无法拆包");
                }
                try {
                    this.splitPkg(snListInfoDto.getPkgNo(), loginName);
                } catch (Exception e) {
                    e.printStackTrace();
                    Shift.fatal(StatusCode.SERVER_MODIFY_ERROR, "拆包错误" + snListInfoDto.getPkgNo());
                }
            }
        } else {
            if (targetLocCode != null) {
                Location targetLocation = locationMapper.selectOne(new QueryWrapper<>(new Location().setLocCode(targetLocCode)));
                targetLocId = targetLocation.getLocId();
            }

            for (SnListInfoDto snListInfoDto : snList) {
                String pkgNo = snListInfoDto.getPkgNo();
                TransferLocationSnDto snTypeAndInfo = this.getSnTypeAndInfoWithSplit(pkgNo);
                if (!snTypeAndInfo.getWhsId().equals(targetWhsId)) {
                    Shift.fatal(StatusCode.INVALID_MODEL_FIELDS, "源条码与目标条码不在同一个仓库");
                }
                //判断每一个条码拆包前是否需要移库
                //如果目标条码库位不为空，源条码库位为空，则触发【移库】，将源条码【上架】
                if (snListInfoDto.getLocCode() == null && targetLocCode != null) {
//                    Shift.fatal(StatusCode.BUSINESS_ERR,"目标条码库位为空，请先【下架】再拆并包");
                    transferLocationService.transferLocation(snListInfoDto.getPkgNo(), targetLocId, loginName, 1, 0, targetWhsId, targetGoods);

                }
                //如果目标条码库位为空，源条码库位不为空，触发【移库】，将源条码【下架】
                if (snListInfoDto.getLocCode() != null && targetLocCode == null) {
                    transferLocationService.transferLocation(snListInfoDto.getPkgNo(), targetLocId, loginName, 1, 0, targetWhsId, targetGoods);
                }
                //目标条码库位和源条码库位都不为空，
                if (snListInfoDto.getLocCode() != null && !(snListInfoDto.getLocCode().equals("")) && targetLocCode != null
                        && (!targetLocCode.equals(""))) {
                    //判断库位是否一致，不一致，触发【移库】
                    transferLocationService.transferLocation(snListInfoDto.getPkgNo(), targetLocId, loginName, 1, 0, targetWhsId, targetGoods);
                }
                this.updatePkgRelationShip(pkgNo, targetSn, loginName);
            }
        }
        //TODO 测试代码，测试和投产记得删除
//        int a = 0 / 0;
        return new Result<>(true, "操作成功");
    }


    /**
     * 更新包装层级
     *
     * @param sourceSn 源条码
     * @param targetSn 目标条码
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void updatePkgRelationShip(String sourceSn, String targetSn, String loginName) {
        TransferLocationSnDto sourceSnDataInfo = this.getSnTypeAndInfoWithSplit(sourceSn);
        TransferLocationSnDto targetSnDataInfo = this.getSnTypeAndInfoWithSplit(targetSn);

        //------------------源条码信息---------------------->
        Goods sourceGoods = sourceSnDataInfo.getGoods();
//        String sourceGoodsSn = sourceSnDataInfo.getGoodsSn();
//        String sourcePkgSn = sourceSnDataInfo.getPkgSn();
        Integer sourcePkgLevel = sourceSnDataInfo.getPkgLevel();
        int sourceSnType = sourceSnDataInfo.getSnType();
//        String sourcePkgUrl = sourceSnDataInfo.getPkgUrl();
        GoodsPkg sourceGoodsPkg = sourceSnDataInfo.getGoodsPkg();


        //------------------目标条码信息--------------------->
//        Goods targetGoods = targetSnDataInfo.getGoods();
//        String targetGoodsSn = targetSnDataInfo.getGoodsSn();
//        String targetPkgSn = targetSnDataInfo.getPkgSn();
        Integer targetPkgLevel = targetSnDataInfo.getPkgLevel();
//        int targetSnType = targetSnDataInfo.getSnType();
//        String targetPkgUrl = targetSnDataInfo.getPkgUrl();
        Integer pkgId = targetSnDataInfo.getPkgId();
        GoodsPkg targetGoodsPkg = targetSnDataInfo.getGoodsPkg();

        //如果包装等级差大于等于2 ，比如源条码goodsSn等级0 ，
        //目标条码是卡版，等级2，中间是纸箱，等级1 ，等级差 = 2
        //则拒绝并包操作
        if ((Math.abs(sourcePkgLevel - targetPkgLevel) >= 2)) {
            Shift.fatal(StatusCode.SERVER_MODIFY_ERROR, "拒绝跨等级拆并包");
        }

        //如果源条码等级大于目标条码，拒绝操作
        if (sourcePkgLevel > targetPkgLevel) {
            Shift.fatal(StatusCode.SERVER_MODIFY_ERROR, "不能将下级包装并入上级");
        }

        if (sourceSnType == 0) {
            //条码
        	sourceGoods.setQaStatus(null); // 临时处理与翻牌冲突的偶发性问题，后续再做优化处理
            sourceGoods.setPkgId(pkgId);
            sourceGoods.setModifyUser(loginName);
            sourceGoods.setModifyTime(new Date());
            goodsMapper.updateById(sourceGoods);
        } else {
            //包装
            sourceGoodsPkg.setFatherPkgId(targetGoodsPkg.getGoodsPkgId());
            sourceGoodsPkg.setModifyTime(new Date());
            sourceGoodsPkg.setModifyUser(loginName);
            goodsPkgMapper.updateById(sourceGoodsPkg);
        }
    }

    /**
     * 拆包 只拆不并
     *
     * @param sourceSn 源条码
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public void splitPkg(String sourceSn, String loginName) {
        TransferLocationSnDto snTypeAndInfo = this.getSnTypeAndInfoWithSplit(sourceSn);
        int snType = snTypeAndInfo.getSnType();
        GoodsPkg goodsPkg = snTypeAndInfo.getGoodsPkg();
        Goods goods = snTypeAndInfo.getGoods();
        if (snType == 0) {
            goods.setPkgId(null);
            goods.setModifyTime(new Date());
            goods.setModifyUser(loginName);
            goodsMapper.updatePkgIdById(goods);
        } else {
            goodsPkg.setFatherPkgId(0);
            goodsPkg.setModifyTime(new Date());
            goodsPkg.setModifyUser(loginName);
            goodsPkgMapper.update(goodsPkg, new UpdateWrapper<>(new GoodsPkg().setGoodsPkgId(goodsPkg.getGoodsPkgId())));
        }
    }
}
